#include"ConnectionPool.h"
#include<thread>
#include<iostream>
#include<functional>
#include <unistd.h>
#include<muduo/base/Logging.h>
using namespace std;



ConnectionPool* ConnectionPool::getConnectionPool()
{
	static ConnectionPool pool;
	return &pool;
}


bool ConnectionPool::loadConfigFile()
{
	FILE* pf = fopen("../config/mysql.conf", "r");
	if (pf == nullptr)
	{
		LOG_FATAL<<"mysql loadConfigFile error: ";
		return false;
	}

	while (!feof(pf))
	{
		char line[1024] = { 0 };
		// 先读一行
		fgets(line, 1024, pf);
		string str = line;
		int idx = str.find('=', 0);
		if (idx == -1) continue;

		int edx = str.find('\n', idx);
		string key = str.substr(0, idx);
		string value = str.substr(idx + 1, edx - idx - 1);

		if (key == "ip")
		{
			_ip = value;
		}
		else if (key == "username")
		{
			_username = value;
		}
		else if (key == "password")
		{
			_password = value;
		}
		else if (key == "dbname")
		{
			_dbname = value;
		}
		else if (key == "port")
		{
			_port = atoi(value.c_str());
		}
		else if (key == "initSize")
		{
			_initSize = atoi(value.c_str());
		}
		else if (key == "maxSize")
		{
			_maxSize = atoi(value.c_str());
		}
		else if (key == "maxIdleTime")
		{
			_maxIdleTime = atoi(value.c_str());
		}
		else if (key == "connectionTimeOut")
		{
			_connectionTimeout = atoi(value.c_str());
		}



	}

	fclose(pf);
	return true;

}


void ConnectionPool::produceConnectionTask()
{
	for (;;)
	{
		unique_lock<mutex> lock(_queueMutex);
		// 队列不为空就阻塞
		while (!_connectionQue.empty())
		{
			cv.wait(lock);
		}

		if (_connectionCnt < _maxSize)
		{
			Connection* p = new Connection();
			p->connect(_ip, _port, _username, _password, _dbname);
			p->refreshAliveTime();
			_connectionQue.push(p);
			_connectionCnt++;
		}

		//通知消费者消费
		cv.notify_all();
	}
}

ConnectionPool::ConnectionPool()
{
	if (!loadConfigFile())
	{
		return;
	}

	for (int i = 0; i < _initSize; i++)
	{
		Connection* p = new Connection();
		p->connect(_ip, _port, _username, _password, _dbname);
		p->refreshAliveTime();
		_connectionQue.push(p);
		_connectionCnt++;
	}

	// 使用绑定器让c++的成员方法 能够c风格传函数 生产连接的线程 循环生产
	thread produce(std::bind(&ConnectionPool::produceConnectionTask, this));
	produce.detach();
	//启动一个新的定时线程，扫描超过maxIdleTime时间的空闲连接，进行对于回收
	thread scanner(std::bind(&ConnectionPool::scannerConnectionTask, this));
	scanner.detach();
}

shared_ptr<Connection> ConnectionPool::getConnection()
{
	unique_lock<mutex> lock(_queueMutex);
	// 如果是一个空的 队列
	while (_connectionQue.empty())
	{
		// 等待一段时间，如果超过了时间或者通知了 
		if (cv_status::timeout == cv.wait_for(lock, chrono::milliseconds(_connectionTimeout)))
		{
			// 如果是超时 被唤醒的话
			if (_connectionQue.empty())
			{
				LOG("获取空闲连接超时....获取失败");
				return nullptr;
			}
		}

	}


	// 自定义shared_ptr的释放方式
	shared_ptr<Connection> sp(_connectionQue.front(), [&](Connection* pcon) {
		// 线程安全
		unique_lock<mutex> lock(_queueMutex);
		pcon->refreshAliveTime();
		_connectionQue.push(pcon);
		});
	_connectionQue.pop();
	// 如果消费完是为空的话 就通知生产
	if (_connectionQue.empty())
	{
		cv.notify_all();	//谁最后消费了最后一个 就唤醒生产 
	}

	return sp;
}




void ConnectionPool::scannerConnectionTask()
{
	for (;;)
	{
		// sleep 定时效果
		this_thread::sleep_for(chrono::seconds(_maxIdleTime));

		unique_lock<mutex>lock(_queueMutex);
		while (_connectionCnt > _initSize)
		{
			Connection* p = _connectionQue.front();
			// 使用毫秒
			if (p->getAliceTime() >= _maxIdleTime * 1000)
			{
				_connectionQue.pop();
				_connectionCnt--;
				delete p;
			}
			else
			{
				// 队头连接 没有超过 那么剩下的也不会超过
				break;
			}
		}
	}
}
